﻿
#include <chrono>
#include <QDataStream>
#include <QDebug>
#include <QSerialPortInfo>
#include <thread>

#include "FingerModule.h"

FingerModule::FingerModule(QObject *parent)
    : QObject(parent),
      m_fingerImg(FINGER_IMG_WIDTH, FINGER_IMG_HEIGHT, QImage::Format_Grayscale8)
{
    m_serial = new QSerialPort(this);
    m_conList.fill(false, m_sysParam.m_totalTempCnt);
    return;
}

FingerModule::~FingerModule()
{
    //closeSerialPort();

    return;
}


bool FingerModule::openSerialPort(qint32 baudRate, QString &errInfo)
{
    if(m_serial && m_serial->isOpen()){
        if(m_processing){
            m_processing = false;
        }
        m_serial->close();
    }

    QString portName;
    QList<QSerialPortInfo> portList = QSerialPortInfo::availablePorts();
    auto it = portList.begin();
    for(; it!=portList.end(); ++it){
        portName = it->portName();
        m_serial->setPortName(portName); //设置端口名称，也就是设备管理器中新增的COM3.
        m_serial->setBaudRate(baudRate); //设置波特率bps，模块默认57600bps.
        m_serial->setParity(QSerialPort::NoParity); //设置校验位，NoParity表示不校验.
        m_serial->setDataBits(QSerialPort::Data8); //设置数据位.
        m_serial->setStopBits(QSerialPort::TwoStop); //设置停止位.
        m_serial->setFlowControl(QSerialPort::NoFlowControl); //设置流控.
        if(!m_serial->open(QIODevice::ReadWrite)){
            errInfo = QString::fromLocal8Bit("[提示]串口%1打开测试失败，原因%2").arg(portName).arg(m_serial->errorString());
            continue;
        }

        QByteArray cmd("\xEF\x01\xFF\xFF\xFF\xFF\x01\x00\x03\x53\x00\x57", 12);
        m_serial->write(cmd);
        m_serial->waitForBytesWritten(200);
        m_serial->waitForReadyRead(REPLY_TIMEOUT_MS);
        QByteArray reply = m_serial->readAll();
        if(0 == checkSingleReply(FingerCmd::GetEcho, errInfo)){
            break;
        }
        m_serial->close();
    }

    if(it == portList.end()){
		errInfo = QString::fromLocal8Bit("[错误]未探测到合法的指纹串口设备");
		emit sigShowMsg(errInfo);
		return false;
    }

	errInfo = QString::fromLocal8Bit("[提示]指纹模块串口%1已打开，波特率%2").arg(portName).arg(baudRate);
    emit sigShowMsg(errInfo);
    return true;
}

void FingerModule::resetModule()
{
    m_processing = false;
    return;
}

int FingerModule::getFreeTempId()
{
    int ret = -1;
    for(int i=0; i<m_conList.size(); ++i){
        if(!m_conList[i]){
            ret = i;
            break;
        }
    }
    return ret;
}

void FingerModule::onSendCtrlCmd(int ctrlCmd, int param1, int param2, int param3, int param4)
{
    QString cmdName = getCmdNameStr(ctrlCmd);
    QString errInfo;
    quint8 packFlag = 0x01; //包标识.
    quint16 packLeng = 0x0003; //包长度
    quint8 cmdCode = ctrlCmd; //指令码
    quint16 checkSum = 0x0000; //校验和
    QByteArray cmd;
    QDataStream stream(&cmd, QIODevice::WriteOnly); //数据流实例
    stream.setByteOrder(QDataStream::BigEndian); //设置字节序
    stream << m_packHead << m_address << packFlag;

    switch(ctrlCmd){
    case FingerCmd::VerifyPassword:
        packLeng = 0x0007;
        stream << packLeng << cmdCode << param1 << checkSum;
        if(-1 == param1){
            errInfo = QString::fromLocal8Bit("[异常]参数错误，验证口令需要1个参数");
        }
        break;
    case FingerCmd::ReadSysParam:
        stream << packLeng << cmdCode << checkSum;
        break;
    case FingerCmd::ReadConList:
        packLeng = 0x0004;
        stream << packLeng << cmdCode << quint8(param1) << checkSum;
        if(-1 == param1){
            errInfo = QString::fromLocal8Bit("[异常]参数错误，FingerCmd::ReadConList需要1个参数:索引页");
        }
        break;
    case FingerCmd::TemplateNum:
        stream << packLeng << cmdCode << checkSum;
        break;
    case FingerCmd::GenImg:
        stream << packLeng << cmdCode << checkSum;
        break;
    case FingerCmd::GetEcho:
        stream << packLeng << cmdCode << checkSum;
        break;
    case FingerCmd::AutoLogin:
        packLeng = 0x0008;
        stream << packLeng << cmdCode << quint16(param1) << quint8(param2) << quint16(param3) << checkSum;
        if(-1 == param1 || -1 == param2 || -1 == param3){
            errInfo = QString::fromLocal8Bit("[异常]参数错误，自动登录需要3个参数:模板ID+录入次数+复用项");
        }
        break;
    case FingerCmd::AutoSearch:
        packLeng = 0x0008;
        stream << packLeng << cmdCode << quint8(param1) << quint16(param2) << quint16(param3) << checkSum;
        if(-1 == param1 || -1 == param2 || -1 == param3){
            errInfo = QString::fromLocal8Bit("[异常]参数错误，自动搜索需要3个参数:安全等级+模板ID/范围+复用项");
        }
        break;
    case FingerCmd::UpImage:
        stream << packLeng << cmdCode << checkSum; //耗时的操作，大概耗时2秒.
        break;
    case FingerCmd::Img2Tz:
        packLeng = 0x0004;
        stream << packLeng << cmdCode << quint8(param1) << checkSum;
        if(-1 == param1){
            errInfo = QString::fromLocal8Bit("[异常]参数错误，生成特征需要1个参数:缓冲区编号，注编号从1起");
        }
        break;
    case FingerCmd::RegModel:
        stream << packLeng << cmdCode << checkSum;
        break;
    case FingerCmd::Store:
        packLeng = 0x0006;
        stream << packLeng << cmdCode << quint8(param1) << quint16(param2) << checkSum;
        if(-1 == param1 || -1 == param2){
            errInfo = QString::fromLocal8Bit("[异常]参数错误，存储模板需要2个参数:缓冲区号+模板编号，注编号从1起");
        }
        break;
    case FingerCmd::DeleteChar:
        packLeng = 0x0007;
        stream << packLeng << cmdCode << quint16(param1) << quint16(param2) << checkSum;
        if(-1 == param1 || -1 == param2){
            errInfo = QString::fromLocal8Bit("[异常]参数错误，删除需要2个参数:模板编号+删除个数，注编号从1起");
        }
        break;
    case FingerCmd::Empty:
        stream << packLeng << cmdCode << checkSum;
        break;
    case FingerCmd::Search:
        packLeng = 0x0008;
        stream << packLeng << cmdCode << quint8(param1) << quint16(param2) << quint16(param3) << checkSum;
        if(-1 == param1 || -1 == param2 || -1 == param3){
            errInfo = QString::fromLocal8Bit("[异常]参数错误，单次搜索指纹需要3个参数:缓冲区号+起始模板号+搜索个数，注编号从1起");
        }
        break;
    case FingerCmd::PsCancel:
        stream << packLeng << cmdCode << checkSum;
        break;
    default: errInfo = QString::fromLocal8Bit("[异常]未知命令类型%1，请检查宏定义及函数%2的分支判断部分").arg(ctrlCmd).arg(__FUNCTION__);
    }

    if(!errInfo.isEmpty()){ //参数个数错误.
        emit sigShowMsg(errInfo);
        return;
    }

    if(nullptr == m_serial || !m_serial->isOpen() || !m_serial->isWritable()){
        errInfo = QString::fromLocal8Bit("[异常]编码错误，指纹设备串口未打开或打开方式错误");
        emit sigShowMsg(errInfo);
        return;
    }
    modifyCheckSum(cmd);

    //带参数返回的指令就需要解析起参数，并通知上层指令已获取.
    switch(ctrlCmd){
    case FingerCmd::ReadSysParam: thReadSysParam(cmd); break;
    case FingerCmd::ReadConList: thReadConList(cmd); break;
    case FingerCmd::TemplateNum: thReadTemplate(cmd); break;
    case FingerCmd::AutoLogin: thAutoLogin(cmd, param1); break;
    case FingerCmd::AutoSearch: thAutoSearch(cmd); break;
    case FingerCmd::UpImage: thUpImage(cmd); break;
    case FingerCmd::Search: thSearch(cmd); break;
    default:
        m_serial->readAll(); //清空应答缓存.
        if(cmd.size() != m_serial->write(cmd)){
            errInfo = QString::fromLocal8Bit("[异常]指令发送失败，原因%1").arg(m_serial->errorString());
            emit sigShowMsg(errInfo);
        }
        else {
            emit sigShowMsg(QString::fromLocal8Bit("[发送]%1指令:%2").arg(cmdName, QString(cmd.toHex(' ')).toUpper()));
            checkSingleReply(ctrlCmd, errInfo);
            emit sigShowMsg(errInfo);
        }
    }

    return ;
}

qint8 FingerModule::checkSingleReply(int ctrlCmd, QString &errInfo)
{
    QString cmdName = getCmdNameStr(ctrlCmd);
    QByteArray reply(12, '\x00');

    m_serial->waitForReadyRead(REPLY_TIMEOUT_MS);
    if(reply.size() > m_serial->bytesAvailable()){
        errInfo = QString::fromLocal8Bit("[异常]指令应答超时，具体原因：%1").arg(m_serial->errorString());
        return -1;
    }

    m_serial->read(reply.data(), reply.size());

    const quint8 *ptr = reinterpret_cast<const quint8 *>(reply.data());
    switch(ptr[9]){
    case 0x00: errInfo = QString::fromLocal8Bit("[接收]%1执行完毕或OK").arg(cmdName); break;
    case 0x01: errInfo = QString::fromLocal8Bit("[错误]数据包接收错误"); break;
    case 0x02: errInfo = QString::fromLocal8Bit("[错误]传感器上没有手指"); break;
    case 0x03: errInfo = QString::fromLocal8Bit("[错误]录入指纹图像失败"); break;
    case 0x06: errInfo = QString::fromLocal8Bit("[错误]指纹图像太乱而生不成特征"); break;
    case 0x07: errInfo = QString::fromLocal8Bit("[错误]指纹图像正常，但特征点太少（或面积太小）而生不成特征"); break;
    case 0x08: errInfo = QString::fromLocal8Bit("[错误]指纹不匹配"); break;
    case 0x09: errInfo = QString::fromLocal8Bit("[错误]没搜索到指纹"); break;
    case 0x0A: errInfo = QString::fromLocal8Bit("[错误]特征合并失败"); break;
    case 0x0B: errInfo = QString::fromLocal8Bit("[错误]访问指纹库时地址序号超出指纹库范围"); break;
    case 0x0C: errInfo = QString::fromLocal8Bit("[错误]从指纹库读模板出错或无效"); break;
    case 0x0D: errInfo = QString::fromLocal8Bit("[错误]上传特征失败"); break;
    case 0x0E: errInfo = QString::fromLocal8Bit("[错误]模块不能接受后续数据包"); break;
    case 0x0F: errInfo = QString::fromLocal8Bit("[错误]上传图像失败"); break;
    case 0x10: errInfo = QString::fromLocal8Bit("[错误]删除模板失败"); break;
    case 0x11: errInfo = QString::fromLocal8Bit("[错误]清空指纹库失败"); break;
    case 0x12: errInfo = QString::fromLocal8Bit("[错误]不能进入低功耗状态"); break;
    case 0x13: errInfo = QString::fromLocal8Bit("[错误]口令不正确"); break;
    case 0x14: errInfo = QString::fromLocal8Bit("[错误]系统复位失败"); break;
    case 0x15: errInfo = QString::fromLocal8Bit("[错误]缓冲区内没有有效原始图而生不成图像"); break;
    case 0x17: errInfo = QString::fromLocal8Bit("[错误]残留指纹或两次采集之间手指没有移动过"); break;
    case 0x18: errInfo = QString::fromLocal8Bit("[错误]读写FLASH 出错"); break;
    case 0x1A: errInfo = QString::fromLocal8Bit("[错误]无效寄存器号"); break;
    case 0x1D: errInfo = QString::fromLocal8Bit("[错误]端口操作失败"); break;
    case 0x1E: errInfo = QString::fromLocal8Bit("[错误]自动注册失败"); break;
    case 0x1F: errInfo = QString::fromLocal8Bit("[错误]指纹库满"); break;
    case 0x20: errInfo = QString::fromLocal8Bit("[错误]地址码错误"); break;
    case 0x21: errInfo = QString::fromLocal8Bit("[错误]必须验证口令"); break;
    case 0x23: errInfo = QString::fromLocal8Bit("[错误]指纹模板为空"); break;
    case 0x26: errInfo = QString::fromLocal8Bit("[错误]超时"); break;
    default: errInfo = QString::fromLocal8Bit("[异常]出现系统保留的错误码");
    }

    errInfo.append(QString::fromLocal8Bit("，应答%1").arg(QString(reply.toHex(' ')).toUpper()));
    return ptr[9];
}

void FingerModule::thReadSysParam(QByteArray cmd)
{
    QString errInfo;
    QByteArray reply;

    m_serial->readAll(); //清空应答缓存.
    if(cmd.size() != m_serial->write(cmd)){
        errInfo = QString::fromLocal8Bit("[异常]指令发送失败，原因%1").arg(m_serial->errorString());
        emit sigShowMsg(errInfo);
        return;
    }

    emit sigShowMsg(QString::fromLocal8Bit("[发送]读系统参数指令:%1").arg(QString(cmd.toHex(' ')).toUpper()));
    m_serial->waitForReadyRead(REPLY_TIMEOUT_MS);
    if(0 == m_serial->bytesAvailable()){
        errInfo = QString::fromLocal8Bit("[异常]指令应答超时，具体原因：%1").arg(m_serial->errorString());
        emit sigShowMsg(errInfo);
        return;
    }

    reply = m_serial->readAll();
    if(reply.size() < 28){
        errInfo = QString::fromLocal8Bit("[异常]指令应答过短，不足以解析！");
        emit sigShowMsg(errInfo);
        return;
    }

    emit sigShowMsg(QString::fromLocal8Bit("[接收]%1应答:%2").arg(getCmdNameStr(FingerCmd::ReadSysParam), QString(reply.toHex(' ')).toUpper()));

    const quint8 *ptr = reinterpret_cast<const quint8 *>(reply.data());

    if(nullptr != ptr && 0 == ptr[9]){
        m_sysParam.m_stateReg = (ptr[10] << 8) | ptr[11];
        m_sysParam.m_sysIdent = (ptr[12] << 8) | ptr[13];
        m_sysParam.m_totalTempCnt = (ptr[14] << 8) | ptr[15];
        m_sysParam.m_safeRank = (ptr[16] << 8) | ptr[17];
        m_sysParam.m_address  = (ptr[18] << 24)|(ptr[19] << 16) | (ptr[20] << 8) | ptr[21];
        m_sysParam.m_packSize = (ptr[22] << 8) | ptr[23];
        m_sysParam.m_bitRate  = (ptr[24] << 8) | ptr[25];
    }

    return;
}

void FingerModule::thReadConList(QByteArray cmd)
{
    QString errInfo;
    QByteArray reply;

    m_serial->readAll(); //清空应答缓存.
    if(cmd.size() != m_serial->write(cmd)){
        errInfo = QString::fromLocal8Bit("[异常]指令发送失败，原因%1").arg(m_serial->errorString());
        emit sigShowMsg(errInfo);
        return;
    }

    emit sigShowMsg(QString::fromLocal8Bit("[发送]读库列表指令:%1").arg(QString(cmd.toHex(' ')).toUpper()));
    m_serial->waitForReadyRead(REPLY_TIMEOUT_MS);
    int loopTime = 0; //新增，防止中途进行了半包解析.
    while(m_serial->bytesAvailable() < 44 && loopTime < 3){
        m_serial->waitForReadyRead(20);
        ++loopTime;
    }

    reply = m_serial->readAll();
    if(reply.size() < 44){
        errInfo = QString::fromLocal8Bit("[异常]指令应答过短，不足以解析！");
        emit sigShowMsg(errInfo);
        return;
    }

    emit sigShowMsg(QString::fromLocal8Bit("[接收]%1应答:%2").arg(getCmdNameStr(FingerCmd::ReadConList), QString(reply.toHex(' ')).toUpper()));

    const quint8 *ptr = reinterpret_cast<const quint8 *>(reply.data());

    if(nullptr != ptr && 0 == ptr[9]){
        int tableSize = (reply.size() - 12) * 8;
        m_conList.fill(false, tableSize);
        m_usedTempCnt = 0;
        ptr += 10;
        for(int i=0; i<44-12; ++i){
            for(int j=0; j<8; ++j){
                if(0 == (ptr[i] & 1<<j)){
                    m_conList[i*8+j] = false;
                }
                else{
                    m_conList[i*8+j] = true;
                    ++m_usedTempCnt;
                }
            }
        }
        emit sigReadConListOver();
    }
}

void FingerModule::thReadTemplate(QByteArray cmd)
{
    QString errInfo;
    QByteArray reply;

    m_serial->readAll(); //清空应答缓存.
    if(cmd.size() != m_serial->write(cmd)){
        errInfo = QString::fromLocal8Bit("[异常]指令发送失败，原因%1").arg(m_serial->errorString());
        emit sigShowMsg(errInfo);
        return;
    }

    emit sigShowMsg(QString::fromLocal8Bit("[发送]读模板数指令:%1").arg(QString(cmd.toHex(' ')).toUpper()));
    m_serial->waitForReadyRead(REPLY_TIMEOUT_MS);
    if(0 == m_serial->bytesAvailable()){
        errInfo = QString::fromLocal8Bit("[异常]指令应答超时，具体原因：%1").arg(m_serial->errorString());
        emit sigShowMsg(errInfo);
        return;
    }

    reply = m_serial->readAll();
    if(reply.size() < 12){
        errInfo = QString::fromLocal8Bit("[异常]指令应答过短，不足以解析！");
        emit sigShowMsg(errInfo);
        return;
    }

    emit sigShowMsg(QString::fromLocal8Bit("[接收]%1应答:%2").arg(getCmdNameStr(FingerCmd::TemplateNum), QString(reply.toHex(' ')).toUpper()));

    const quint8 *ptr = reinterpret_cast<const quint8 *>(reply.data());
    if(nullptr != ptr && 0 == ptr[9]){
        m_usedTempCnt = (ptr[10] << 8) | ptr[11];
    }
    return;
}

void FingerModule::thAutoLogin(QByteArray cmd, int tempId)
{
    const char syncHead[2] = {'\xEF', '\x01'};
    const int headSize = 9;
    int packLen = 0;
    QByteArray reply(14, '\x00');
    const unsigned char *ptr = reinterpret_cast<const unsigned char *>(reply.data());
    QString sureStr, stageStr, tipStr;
    QString errInfo;

    if(cmd.size() != m_serial->write(cmd)){
        errInfo = QString::fromLocal8Bit("[异常]指令发送失败，原因%1").arg(m_serial->errorString());
        emit sigShowMsg(errInfo);
        return;
    }

    emit sigShowMsg(QString::fromLocal8Bit("[发送]自动登记指令:%1").arg(QString(cmd.toHex(' ')).toUpper()));
    m_processing = true;
    while(m_processing){
        if(headSize > m_serial->bytesAvailable()){
            m_serial->waitForReadyRead(10);
            continue;
        }
        m_serial->read(reply.data(), headSize);
        if(0 != memcmp(syncHead, ptr, 2)) continue;
        packLen = ptr[7] << 8 | ptr[8];
        while(m_processing && packLen > m_serial->bytesAvailable()){
            m_serial->waitForReadyRead(10);
            continue;
        }
        packLen = m_serial->read(reply.data()+headSize, packLen);
        if(14 != headSize + packLen) continue;
        if(false == m_processing) break;

        switch(ptr[9]){ //确认码.
        case 0x000: sureStr = QString::fromLocal8Bit("成功"); break;
        case 0x007: sureStr = QString::fromLocal8Bit("生成特征失败"); break;
        case 0x00A: sureStr = QString::fromLocal8Bit("合并特征失败"); break;
        case 0x00B: sureStr = QString::fromLocal8Bit("ID号超出范围"); break;
        case 0x01F: sureStr = QString::fromLocal8Bit("指纹库已满"); break;
        case 0x022: sureStr = QString::fromLocal8Bit("指纹模板非空"); break;
        case 0x025: sureStr = QString::fromLocal8Bit("录入次数设置错误"); break;
        case 0x026: sureStr = QString::fromLocal8Bit("超时"); break;
        case 0x027: sureStr = QString::fromLocal8Bit("指纹已存在"); break;
        }

        switch(ptr[10]){ //阶段标记.
        case 0x000: stageStr = QString::fromLocal8Bit("指纹合法性检测"); break;
        case 0x001: stageStr = QString::fromLocal8Bit("获取图像"); break;
        case 0x002: stageStr = QString::fromLocal8Bit("生成特征"); break;
        case 0x003: stageStr = QString::fromLocal8Bit("判断手指离开"); break;
        case 0x004: stageStr = QString::fromLocal8Bit("合并特征"); break;
        case 0x005: stageStr = QString::fromLocal8Bit("注册检验"); break;
        case 0x006: stageStr = QString::fromLocal8Bit("存储模板"); break;
        }

        switch(ptr[11]){ //备注.
        case 0x000: tipStr = QString::fromLocal8Bit("指纹合法性检测"); break;
        case 0x0F0: tipStr = QString::fromLocal8Bit("合并特征"); break;
        case 0x0F1: tipStr = QString::fromLocal8Bit("检验该手指是否已注册"); break;
        case 0x0F2: tipStr = QString::fromLocal8Bit("存储模板"); break;
        default : tipStr = QString::fromLocal8Bit("当前录入第%1次数").arg(int(ptr[11]));
        }

        emit sigShowMsg(QString::fromLocal8Bit("[接收]%1应答:%2").arg(getCmdNameStr(FingerCmd::AutoLogin), QString(reply.toHex(' ')).toUpper()));
        if(0x00 == ptr[9] && 0x06 == ptr[10]){
            errInfo = QString::fromLocal8Bit("注册成功:%1 %2 %3").arg(stageStr, sureStr, tipStr);
            emit sigShowMsg(errInfo);
            emit sigAutoLoginOver(true, tempId, errInfo);
            onSendCtrlCmd(FingerCmd::PsCancel);
            onSendCtrlCmd(FingerCmd::ReadConList, 0);
            break;
        }

        if(0x00 != ptr[9]){
            errInfo = QString::fromLocal8Bit("注册失败:%1 %2 %3").arg(stageStr, sureStr, tipStr);
            emit sigShowMsg(errInfo);
            std::this_thread::sleep_for(std::chrono::milliseconds(200));
            if (m_processing) m_serial->write(cmd);
        }
        else{
            errInfo = QString::fromLocal8Bit("注册中:%1 %2 %3").arg(stageStr, sureStr, tipStr);
            emit sigShowMsg(errInfo);
            emit sigAutoLoginOver(false, tempId, errInfo);
        }

    }
    m_processing = false;
    return;
}

void FingerModule::thAutoSearch(QByteArray cmd)
{
    const char syncHead[2] = {'\xEF', '\x01'};
    const int headSize = 9;
    int packLen = 0;
    QByteArray reply(17, '\x00');
    const unsigned char *ptr = reinterpret_cast<const unsigned char *>(reply.data());
    QString sureStr, stageStr, tipStr;
    QString errInfo;

    m_serial->readAll(); //清空应答缓存.
    if(cmd.size() != m_serial->write(cmd)){
        errInfo = QString::fromLocal8Bit("[异常]指令发送失败，原因%1").arg(m_serial->errorString());
        emit sigShowMsg(errInfo);
        return;
    }

    emit sigShowMsg(QString::fromLocal8Bit("[发送]自动搜索指令:%1").arg(QString(cmd.toHex(' ')).toUpper()));
    m_processing = true;
    while(m_processing){
        if(headSize > m_serial->bytesAvailable()){
            m_serial->waitForReadyRead(10);
            continue;
        }
        m_serial->read(reply.data(), headSize);
        if(0 != memcmp(syncHead, ptr, 2)) continue;
        packLen = ptr[7] << 8 | ptr[8];
        while(m_processing && packLen > m_serial->bytesAvailable()){
            m_serial->waitForReadyRead(10);
            continue;
        }
        packLen = m_serial->read(reply.data()+headSize, packLen);
        if(17 != headSize + packLen) continue;
        if(false == m_processing) break;

        switch(ptr[9]){ //确认码.
        case 0x000: sureStr = QString::fromLocal8Bit("成功"); break;
        case 0x001: sureStr = QString::fromLocal8Bit("失败"); break;
        case 0x007: sureStr = QString::fromLocal8Bit("生成特征失败"); break;
        case 0x009: sureStr = QString::fromLocal8Bit("合并特征失败"); break;
        case 0x00B: sureStr = QString::fromLocal8Bit("ID号超出范围"); break;
        case 0x017: sureStr = QString::fromLocal8Bit("残留指纹"); break;
        case 0x023: sureStr = QString::fromLocal8Bit("指纹模板非空"); break;
        case 0x024: sureStr = QString::fromLocal8Bit("指纹库为空"); break;
        case 0x026: sureStr = QString::fromLocal8Bit("超时"); break;
        case 0x027: sureStr = QString::fromLocal8Bit("指纹已存在"); break;
        }

        switch(ptr[10]){ //阶段标记.
        case 0x000: stageStr = QString::fromLocal8Bit("指纹合法性检测"); break;
        case 0x001: stageStr = QString::fromLocal8Bit("获取图像"); break;
        case 0x005: stageStr = QString::fromLocal8Bit("已注册指纹对比"); break;
        }

        int tempId = (ptr[11] << 8) | ptr[12];
        //int score = (ptr[13] << 8) | ptr[14];

        emit sigShowMsg(QString::fromLocal8Bit("[接收]%1应答:%2").arg(getCmdNameStr(FingerCmd::AutoSearch), QString(reply.toHex(' ')).toUpper()));

        if(0x00 == ptr[9] && 0x05 == ptr[10]){
            errInfo = QString::fromLocal8Bit("搜索成功:%1 %2 %3").arg(stageStr, sureStr, tipStr);
            emit sigShowMsg(errInfo);
            emit sigAutoSearchOver(true, tempId, errInfo);
            std::this_thread::sleep_for(std::chrono::milliseconds(800));
            if(m_processing) m_serial->write(cmd);
        }
        if(0x00 != ptr[9]){
            errInfo = QString::fromLocal8Bit("搜索失败:%1 %2 %3").arg(stageStr, sureStr, tipStr);
            emit sigShowMsg(errInfo);
            emit sigAutoSearchOver(false, tempId, errInfo);
            std::this_thread::sleep_for(std::chrono::milliseconds(200));
            if (m_processing) m_serial->write(cmd);
        }
        else{
            errInfo = QString::fromLocal8Bit("搜索中:%1 %2 %3").arg(stageStr, sureStr, tipStr);
            emit sigShowMsg(errInfo);
        }
    }
    m_processing = false;
    return;
}

void FingerModule::thUpImage(QByteArray cmd)
{
    QString errInfo;
    QByteArray head(9, char('0x00'));
    QByteArray pack;
    quint8 *ptr = nullptr;
    //int imgW = m_fingerImg.width();
    //int imgH = m_fingerImg.height();
    int col = 0, row = 0;
    int offset = 0;
    uint rgb = 0;
    uchar *pixel = nullptr;

    m_serial->readAll(); //清空应答缓存.
    if(cmd.size() != m_serial->write(cmd)){
        errInfo = QString::fromLocal8Bit("[异常]指令发送失败，原因%1").arg(m_serial->errorString());
        emit sigShowMsg(errInfo);
        return;
    }

    emit sigShowMsg(QString::fromLocal8Bit("[发送]上传图像指令:%1").arg(QString(cmd.toHex(' ')).toUpper()));
    if(0 != checkSingleReply(FingerCmd::UpImage, errInfo)){ //收第1包应答并检查确认码.
        emit sigShowMsg(errInfo);
        return;
    }

    m_processing = true;
    while(m_processing){ //收后续数据包.
        if(head.size() > m_serial->bytesAvailable()){ //收包头.
            m_serial->waitForReadyRead(1);
            std::this_thread::sleep_for(std::chrono::milliseconds(1));
            continue;
        }

        ptr = reinterpret_cast<quint8 *>(head.data());
        m_serial->read(head.data(), head.size());
        int packLen = (ptr[7]<<8) | ptr[8];
        //emit sigShowMsg()
        while(m_processing && packLen > m_serial->bytesAvailable()){ //收包数据.
            m_serial->waitForReadyRead(1);
            std::this_thread::sleep_for(std::chrono::milliseconds(1));
            continue;
        }

        pack.resize(packLen);
        ptr = reinterpret_cast<quint8 *>(pack.data());
        m_serial->read(pack.data(), pack.size());

        for(int j=0; j<packLen-2; ++j){
            if(offset +2 > FINGER_IMG_WIDTH * FINGER_IMG_HEIGHT) break;
            col = offset % FINGER_IMG_WIDTH;
            row = offset / FINGER_IMG_WIDTH;
            rgb = ((ptr[j] & 0xF0) >> 4)* 256 / 16;
            pixel = m_fingerImg.scanLine(row);
            pixel[col] = rgb;

            col = (offset+1) % FINGER_IMG_WIDTH;
            row = (offset+1) / FINGER_IMG_WIDTH;
            rgb = (ptr[j] & 0x0F) * 256 / 16;
            pixel = m_fingerImg.scanLine(row);
            pixel[col] = rgb;

            offset += 2;
        }

        ptr = reinterpret_cast<quint8 *>(head.data());
        if(0x02 == ptr[6]){
            continue;
        }
        else if(0x08 == ptr[6]){
            emit sigUpImageOver();
            //m_fingerImg.save("./test.raw");
            break;
        }
        break;
    }
    m_processing = false;
    return ;
}

void FingerModule::thSearch(QByteArray cmd)
{
    QString errInfo;
    QByteArray reply;

    m_serial->readAll(); //清空应答缓存.
    if(cmd.size() != m_serial->write(cmd)){
        errInfo = QString::fromLocal8Bit("[异常]指令发送失败，原因%1").arg(m_serial->errorString());
        emit sigShowMsg(errInfo);
        return;
    }

    emit sigShowMsg(QString::fromLocal8Bit("[发送]单次搜索指令:%1").arg(QString(cmd.toHex(' ')).toUpper()));
    m_serial->waitForReadyRead(REPLY_TIMEOUT_MS);
    if(0 == m_serial->bytesAvailable()){
        errInfo = QString::fromLocal8Bit("[异常]指令应答超时，具体原因：%1").arg(m_serial->errorString());
        emit sigShowMsg(errInfo);
        return;
    }

    reply = m_serial->readAll();
    if(reply.size() < 16){
        errInfo = QString::fromLocal8Bit("[异常]指令应答过短，不足以解析！");
        emit sigShowMsg(errInfo);
        return;
    }

    emit sigShowMsg(QString::fromLocal8Bit("[接收]%1应答:%2").arg(getCmdNameStr(FingerCmd::Search), QString(reply.toHex(' ')).toUpper()));
    const quint8 *ptr = reinterpret_cast<const quint8 *>(reply.data());
    if(nullptr != ptr && 0 == ptr[9]){
        m_searchPage = (ptr[10] << 8) | ptr[11];
        m_searchScore = (ptr[12] << 8) | ptr[13];
    }
    return;
}

void FingerModule::modifyCheckSum(QByteArray &cmd)
{
    quint16 sum = 0;
    const int len = cmd.size();
    const quint8 *ptr = reinterpret_cast<const quint8 *>(cmd.data());
    for(int i=6; i<len-2; ++i){
        sum += ptr[i];
        if(len-3 == i){
            cmd[len-2] = (sum & 0x0FF00) >> 8;
            cmd[len-1] = (sum & 0x000FF);
        }
    }
    return;
}

QString FingerModule::getCmdNameStr(int ctrlCmd)
{
    QString cmdName;
    switch(ctrlCmd){
    case VerifyPassword: cmdName = QString::fromLocal8Bit("效验口令"); break;
    case ReadConList: cmdName = QString::fromLocal8Bit("读指纹模板索引表"); break;
    case TemplateNum: cmdName = QString::fromLocal8Bit("读指纹模板数"); break;
    case GenImg: cmdName = QString::fromLocal8Bit("录指纹图像"); break;
    case GetEcho: cmdName = QString::fromLocal8Bit("握手"); break;
    case AutoLogin: cmdName = QString::fromLocal8Bit("自动登记"); break;
    case AutoSearch: cmdName = QString::fromLocal8Bit("自动搜索"); break;
    case UpImage: cmdName = QString::fromLocal8Bit("上传图像"); break;
    case Img2Tz: cmdName = QString::fromLocal8Bit("图像转特征"); break;
    case RegModel: cmdName = QString::fromLocal8Bit("特征合成模板"); break;
    case Store: cmdName = QString::fromLocal8Bit("存储模板"); break;
    case DeleteChar: cmdName = QString::fromLocal8Bit("删除模板"); break;
    case Empty: cmdName = QString::fromLocal8Bit("清空指纹库"); break;
    case Search: cmdName = QString::fromLocal8Bit("搜索指纹"); break;
    case PsCancel: cmdName = QString::fromLocal8Bit("取消指令"); break;
    }
    return cmdName;
}





